﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Configuration;
using System.ComponentModel;
using FogOutlook.Plugins.Classes;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Deployment.Application;

namespace FogOutlook {
	public partial class ThisAddIn {

		internal static Microsoft.Office.Interop.Outlook.Application app = null;
		internal static BackgroundWorker bw = null;
		internal static BugTrackerBase BugTracker = null;
		private static ProgressForm pf = null;
		public static bool IsConnected { get { return BugTracker != null; } }
		public static bool IsConnecting { get { return bw != null; } }
		
		private void ThisAddIn_Startup(object sender, System.EventArgs e) {
			app = this.Application;
			EncryptConfigSection("userSettings/FogOutlook.Properties.Settings");
			if(Properties.Settings.Default.checkForUpdates) {
				if(Properties.Settings.Default.remindMeLater > 0) {
					--Properties.Settings.Default.remindMeLater;
					Properties.Settings.Default.Save();
				}
				else {
					WebClient webClient = new WebClient();
					webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(wc_UpdateCompleted);
					webClient.DownloadDataAsync(new Uri("http://fogoutlook.googlecode.com/svn/trunk/FogOutlook/version.xml"));
				}
			}
			PluginLoader.Load(System.Reflection.Assembly.GetExecutingAssembly());
			TryConnect(true, Properties.Settings.Default.username, Properties.Settings.Default.password, Properties.Settings.Default.btURL, Properties.Settings.Default.bttype);
		}

		private void ThisAddIn_Shutdown(object sender, System.EventArgs e) {
		}

		#region VSTO generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InternalStartup() {
			this.Startup += new System.EventHandler(ThisAddIn_Startup);
			this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
		}

		#endregion

	/*	protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject() {
			Office.IRibbonExtensibility ext = base.CreateRibbonExtensibilityObject();
			ext.GetCustomUI(("TabMail");
		}*/

		private void EncryptConfigSection(string sectionKey) {
			Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
			ConfigurationSection section = config.GetSection(sectionKey);
			if(section != null) {
				if(!section.SectionInformation.IsProtected) {
					if(!section.ElementInformation.IsLocked) {
						section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
						section.SectionInformation.ForceSave = true;
						config.Save(ConfigurationSaveMode.Full);
					}
				}
			}
		}

		public static bool WaitConnect() {
			if(IsConnecting) {
				using(pf = new ProgressForm()) {
					if(pf.ShowDialog(app.Parent) == DialogResult.Cancel) {
						bw.CancelAsync();
						return false;
					}
					pf = null;
				}
			}
			if(!IsConnected)
				return false;
			return true;
		}

		public static bool TryConnect(bool init, string username, string password, string url, string bugtracker) {
			if(username.Length == 0) {
				if(!init)
                    MessageBox.Show("Missing username");
				return false;
			}
			if(password.Length == 0) {
                if(!init)
				    MessageBox.Show("Missing password");
				return false;
			}
			if(url.Length == 0) {
                if(!init)
				    MessageBox.Show("Missing url");
				return false;
			}
			if(bugtracker.Length == 0) {
                if(!init)
				    MessageBox.Show("Missing bugtracker selection");
				return false;
			}
			if(bw != null) {
				bw.CancelAsync();
				int i=0;
				while(bw != null && i<500) // We're impatient enough heh...
					Thread.Sleep(100);
				if(bw != null) {
					bw.Dispose(); // ?? Does this work or is there another way to kill a bgw?
					bw = null;
				}
			}
			if(bw == null) {
				bw = new BackgroundWorker();
				bw.WorkerSupportsCancellation = true;
				bw.DoWork += new DoWorkEventHandler(bw_DoWork);
				bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
				bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
				bw.WorkerReportsProgress = true;
				bw.RunWorkerAsync(new object[] { url, username, password, bugtracker });
				//FogOutlook.Properties.Settings.Default.Save(); Only save upon successful, so doing it in rowrkercompleted
			}
			return true;
		}

		private static void bw_DoWork(object sender, DoWorkEventArgs e) {
			BackgroundWorker worker = sender as BackgroundWorker;
			object[] arg = (object[])e.Argument;
			try {
				BugTrackerBase bt;
				if(PluginLoader.Plugins.TryGetValue(arg[3].ToString(), out bt)) {
					bt.Initialize(arg[0].ToString(), arg[1].ToString(), arg[2].ToString(), bw);
					e.Result = bt;
				}
				else {
					e.Result = "Failed to load bugtracker DLL: " + arg[3].ToString();
					BugTracker = null;
				}
				e.Cancel = bw.CancellationPending;
			}
			catch(Exception ex) {
				if(ex.InnerException != null)
					e.Result = ex.Message + ": " + ex.InnerException.Message;
				else
					e.Result = ex.Message;
			}
		}

		private static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) {
			if(pf != null) {
				if(pf.progressBar1.InvokeRequired) // Thought some of the purpose of BGW was to not have to do this... :o
					pf.progressBar1.Invoke(new MethodInvoker(delegate { pf.progressBar1.Value = e.ProgressPercentage; }));
				else
					pf.progressBar1.Value = e.ProgressPercentage;
			}
		}

		private static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
			bw.Dispose();
			bw = null;
			if(e.Cancelled == false && e.Error == null) {
				if(e.Result is BugTrackerBase) {
					BugTracker = (BugTrackerBase)e.Result;
					BugTracker.ShowConfirmation = Properties.Settings.Default.showConfirmation;
					System.Drawing.Bitmap bmp = Properties.Resources.FogOutlook;
					BugTracker.SetIcon(System.Drawing.Icon.FromHandle(bmp.GetHicon()));
					Properties.Settings.Default.Save(); // Save settings upon succesful connect....
				}
				else if(e.Result != null) {
					BugTracker = null;
					MessageBox.Show(e.Result.ToString(), "FogOutlook");
				}
			}
			if(pf != null) {
				if(pf.InvokeRequired) { // Thought some of the purpose of BGW was to not have to do this... :o
					pf.progressBar1.Invoke(new MethodInvoker(delegate {
						pf.DialogResult = DialogResult.OK;
						pf.Dispose();
						pf = null;
					}));
				}
				else {
					pf.DialogResult = DialogResult.OK;
					pf.Dispose();
					pf = null;
				}
			}				
		}

		public static string GetSenderSMTPAddress(Outlook.MailItem mail) {
			string PR_SMTP_ADDRESS = @"http://schemas.microsoft.com/mapi/proptag/0x39FE001E";
			if(mail == null)
				throw new ArgumentNullException();
			if(mail.SenderEmailType == "EX") {
				if (String.Compare((mail as dynamic).Application.Version.Substring(0, 2), "13") < 0) {
					return GetSenderExchangeSmtpAddress2007OrEarlier(mail);
                }
			
				Outlook.AddressEntry sender = mail.Sender;
				if(sender != null) {
					if(sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry || sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeRemoteUserAddressEntry) {
						Outlook.ExchangeUser user = sender.GetExchangeUser(); //ExchangeUser.PrimarySmtpAddress should contain a proper external email
						if(user != null)
							return user.PrimarySmtpAddress;
						else
							return null;
					}
					else
						return sender.PropertyAccessor.GetProperty(PR_SMTP_ADDRESS) as string;
				}
				else
					return null;
			}
			else
				return mail.SenderEmailAddress;
		}

		private static string GetSenderExchangeSmtpAddress2007OrEarlier(Outlook.MailItem mailItem) {
			string smtpAddress = string.Empty;
			try {
				Outlook.Recipient recipient = mailItem.Application.Session.CreateRecipient(mailItem.SenderEmailAddress);
				if (recipient != null && recipient.Resolve() && recipient.AddressEntry != null) {
					smtpAddress = (recipient.AddressEntry.GetExchangeUser() as Outlook.ExchangeUser).PrimarySmtpAddress;
				}
			}
			catch (Exception) {
				smtpAddress = "Error resolving SMTP address.";
			}
			return smtpAddress;
		}
		
		public static void wc_UpdateCompleted(object sender, DownloadDataCompletedEventArgs e) {
			if(e.Cancelled == false && e.Error == null) { // Didn't fail...
				byte[] bytes = e.Result;
				try {
					using(MemoryStream ms = new MemoryStream(bytes)) {
						using(XmlTextReader reader = new XmlTextReader(ms)) {
							XmlDocument doc = new XmlDocument();
							doc.Load(reader);
							string str = doc.InnerText;
							Version latest = new Version(str);
							Version ver = null;
							if(ApplicationDeployment.IsNetworkDeployed) {
								ver = ApplicationDeployment.CurrentDeployment.CurrentVersion;
								if(latest > ver) {
									using(UpdateForm uf = new UpdateForm()) {
										if(uf.InvokeRequired)
											uf.Invoke(new MethodInvoker(delegate { uf.ShowDialog(); }));
										else
											uf.ShowDialog();
									}
								}
							}
						}
					}	
				}
				catch(Exception ex) { // Just ignore, we don't care if check for updates fail
					System.Diagnostics.Debug.WriteLine(ex.Message);
				}
			}
		}
	}
}
